/*---------------------------------------------------------------------------------

	Simple demonstration of sprites using textured quads

---------------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gccore.h>
#include <wiiuse/wpad.h>
#include <fat.h>
#include "gfxUtils.h"
extern "C" {
#include "sudoku.h"
}

//#include "ballsprites_bin.h"
#include "tiles_bin.h"

typedef struct SudoCell
{
	s16 value;
	unsigned int state;
} SudoGrid[9][9];

// GIVEN
#define GIVEN		(1<<0)
#define INCORRECT	(1<<1)
#define UNCERTAIN	(1<<2)

SudoGrid curGrid;
int curX = 0;
int curY = 0;

#define THREADSTACK 512*1024	/*** Default 8k stack space ***/
//lwpq_t ThreadOneQ;
u8 threadonestack[THREADSTACK];

struct sudoku_info sudoku;

static void * threadone (void *arg)
{
	srand(time(NULL));
//  LWP_InitQueue (&ThreadOneQ);
	sud_struct_zero(&sudoku);
	fill(&sudoku);
	//~ display_grid(&sudoku);
//printf("thin\n");
	thin(&sudoku);
//	display_grid(&sudoku);
//	sleep(1);
  return NULL;
}

//void drawSpriteTex( int x, int y, int width, int height, int image, f32 angle );
void drawSudoGrid(SudoGrid grid);
void drawSudoCursor(int x, int y);
void sudoCheck(SudoGrid grid);

void resetGame()
{
	lwp_t ThreadOneT;
	memcpy(threadonestack+THREADSTACK-10, "canary", 6);
	/*** Create Thread ***/
	LWP_CreateThread (&ThreadOneT, threadone, NULL, threadonestack, THREADSTACK, 128);
	int y;
	if(memcmp(threadonestack+THREADSTACK-10, "canary", 6))
	{
		for(y=0;y<9;++y)
		{
			int x;
			for(x=0;x<9;++x)
			sudoku.grid[y][x]=9;
		}	
	}

	for(y=0;y<9;++y)
	{
		int x;
		for(x=0;x<9;++x)
		{
			curGrid[y][x].value = sudoku.grid[y][x];
			curGrid[y][x].state = sudoku.grid[y][x]==0?0:GIVEN;
		}
	}
}

//---------------------------------------------------------------------------------
int main( int argc, char **argv ){
//---------------------------------------------------------------------------------
	
	VideoInit();
	WPAD_Init();
// 	WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR);
//	WPAD_SetVRes(WPAD_CHAN_ALL, rmode->fbWidth, rmode->xfbHeight);
	
	GraphicsInit();

	const png_byte* testPtr = reinterpret_cast<png_byte const*>(tiles_bin);
	loadPngTiles( testPtr , 4, 4);

	// initialize the wii SD card fat interface.
	fatInitDefault();
	fatSetDefaultInterface(PI_INTERNAL_SD);
	// now I can fopen, etc

	char savePath[50];
	getcwd(savePath, 50);
	strcat(savePath, "/save.bin");

	FILE* saveFile = fopen(savePath, "rb");
	if(saveFile)
	{
		char buffer[81];
		fread(buffer, 1, 81, saveFile);
		fclose(saveFile);
		saveFile = NULL;		
		int y;
		int i = 0;
		for(y=0;y<9;++y)
		{
			int x;
			for(x=0;x<9;++x)
			{
				curGrid[y][x].value = buffer[i]&0x3F;
				curGrid[y][x].state = buffer[i]&0x80?GIVEN:0;
				curGrid[y][x].state|= buffer[i]&0x40?UNCERTAIN:0;
				++i;
			}
		}
	}
	else
	{
		resetGame();
	}

	while(1) {

		WPAD_ScanPads();
//		WPAD_IR(0, &ir);
		u32 btns = WPAD_ButtonsDown(0);

		if (btns & WPAD_BUTTON_HOME)
		{
			VIDEO_SetBlack(TRUE);
//			WPAD_Shutdown();
			exit(0);
		}
		if(btns & WPAD_BUTTON_UP)
		{
			if(--curX==-1) curX=8;
		}
		if(btns & WPAD_BUTTON_DOWN)
		{
			if(++curX==9) curX=0;
		}
		if(btns & WPAD_BUTTON_LEFT)
		{
			if(++curY==9) curY=0;
		}
		if(btns & WPAD_BUTTON_RIGHT)
		{
			if(--curY==-1) curY=8;
		}
		if(btns & WPAD_BUTTON_1)
		{
			if( !(curGrid[curY][curX].state&GIVEN) && (--curGrid[curY][curX].value==-1) ) curGrid[curY][curX].value=9;
		}
		if(btns & WPAD_BUTTON_2)
		{
			if( !(curGrid[curY][curX].state&GIVEN) && (++curGrid[curY][curX].value==10) ) curGrid[curY][curX].value=0;
		}
		if(btns & WPAD_BUTTON_PLUS)
		{
			resetGame();
		}
		if(btns & WPAD_BUTTON_MINUS)
		{
			FILE* saveFile = fopen(savePath, "wb");
			if(saveFile)
			{
				char buffer[81];
				int y;
				int i = 0;
				for(y=0;y<9;++y)
				{
					int x;
					for(x=0;x<9;++x)
					{
						buffer[i] = curGrid[y][x].value;
						buffer[i]|= curGrid[y][x].state&GIVEN?		0x80:0;
						buffer[i]|= curGrid[y][x].state&UNCERTAIN?	0x40:0;
						++i;
					}
				}
				fwrite(buffer, 1, 81, saveFile);
				fclose(saveFile);
				saveFile = NULL;		
			}
		}
		if(btns & WPAD_BUTTON_A)
		{
			curGrid[curY][curX].state ^= UNCERTAIN;
		}
				

		sudoCheck(curGrid);
		drawSudoGrid(curGrid);
		drawSudoCursor(curX,curY);

		renderToFrameBuffer(getSpareFrameBuffer());
		swapFrameBuffers();
		waitVSync();
	}
	return 0;
}

/*
//---------------------------------------------------------------------------------
void drawSpriteTex( int x, int y, int width, int height, int image, f32 angle ) {
//---------------------------------------------------------------------------------

	// Set up transform
	Mtx23 transform;
	guMtx23RotDeg(transform, angle);
	guMtx23ScaleApply(transform, transform, width, height);
	guMtx23TransApply(transform, transform, x-width/2.0f, y-width/2.0f);
	
	// set up unity quad
	Mtx24 quad;
	guMtx24Concat(transform, unitQuad, quad);

	int texIndex = image * 8;

	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);			// Draw A Quad
		GX_Position2f32(quad[0][0], quad[1][0]);					// Top Left
		GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
		texIndex+=2;
		GX_Position2f32(quad[0][1], quad[1][1]);			// Top Right
		GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
		texIndex+=2;
		GX_Position2f32(quad[0][2], quad[1][2]);	// Bottom Right
		GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
		texIndex+=2;
		GX_Position2f32(quad[0][3], quad[1][3]);			// Bottom Left
		GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
	GX_End();									// Done Drawing The Quad 

}
*/

void sudoCheck(SudoGrid grid)
{
	int y, x;
	// clear invalid status.. check again
	for(y=0; y<9;++y)
	for(x=0;x<9;++x)
	{
		grid[y][x].state&=~INCORRECT;
	}
	// check duplicates in rows
	for(y=0; y<9;++y)
	for(x=0; x<8;++x)
	{
		if(grid[y][x].value!=0)
		{
			int x2;
			for(x2=x+1;x2<9;++x2)
			{
				if(grid[y][x].value==grid[y][x2].value)
				{
					grid[y][x ].state|=INCORRECT;
					grid[y][x2].state|=INCORRECT;
				}
			}
		}
	}

	// check duplicates in columns
	for(x=0; x<9;++x)
	for(y=0;y<8;++y)
	{
		if(grid[y][x].value!=0)
		{
			int y2;
			for(y2=y+1;y2<9;++y2)
			{
				if(grid[y][x].value==grid[y2][x].value)
				{
					grid[y ][x].state|=INCORRECT;
					grid[y2][x].state|=INCORRECT;
				}
			}
		}
	}

	for(y=0;y<9;y+=3)
	for(x=0;x<9;x+=3)
	{
		// check 3x3 block duplicates
		int x2,y2;
		for(y2=0; y2<3; ++y2)
		for(x2=0; x2<3; ++x2)
		{
			if(grid[y+y2][x+x2].value!=0)
			{
				int x3=x2;
				int y3=y2;
				for(;y3<3;y3++)
				{
					for(;x3<3;x3++)
					{
						if( y2!=y3 || x2!=x3)
						if(grid[y+y2][x+x2].value==grid[y+y3][x+x3].value)
						{
							grid[y+y2][x+x2].state|=INCORRECT;
							grid[y+y3][x+x3].state|=INCORRECT;
						}
					}
					x3 = 0;
				}
				
			}
		}
	}

}

void drawSudoGrid(SudoGrid grid)
{
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define TILE_WIDTH  32
#define TILE_HEIGHT 32
#define INTER_SPACE 5
	int yPos = (SCREEN_HEIGHT-9*TILE_HEIGHT-2*INTER_SPACE)/2;
	int y;
	for(y=0; y<9;++y)
	{
		int xPos = (SCREEN_WIDTH-9*TILE_WIDTH-2*INTER_SPACE)/2;
		if(y==3||y==6)
		{
			yPos+=INTER_SPACE;
		}
		int x;
		for(x=0;x<9;++x)
		{
			if(x==3||x==6)
			{
				xPos+=INTER_SPACE;
			}
			// draw background tile first
			int tile;
// = (grid[y][x].state&GIVEN)?0x0C:(grid[y][x].state&INCORRECT)?0x0D:0x0A;
			if(grid[y][x].state&GIVEN)
			{
				tile = 0x0C;
			}
			else
			{
				tile = (grid[y][x].state&INCORRECT)?0x0D:0x0A;
				if(grid[y][x].state&UNCERTAIN)
					tile++;
			}
			drawTile(xPos, yPos, TILE_WIDTH, TILE_HEIGHT, tile, 0);
			if(grid[y][x].value!=0)
			{
				drawTile(xPos, yPos, TILE_WIDTH, TILE_HEIGHT, grid[y][x].value, 0);
			}
			xPos+=TILE_WIDTH;
		}
		yPos+=TILE_HEIGHT;
	}
}

void drawSudoCursor(int x, int y)
{
	int yPos = (SCREEN_HEIGHT-9*TILE_HEIGHT-2*INTER_SPACE)/2;
	int xPos = (SCREEN_WIDTH-9*TILE_WIDTH-2*INTER_SPACE)/2;
	xPos+=x*TILE_WIDTH;
	if(x>=3) xPos+=INTER_SPACE;
	if(x>=6) xPos+=INTER_SPACE;
	yPos+=y*TILE_HEIGHT;
	if(y>=3) yPos+=INTER_SPACE;
	if(y>=6) yPos+=INTER_SPACE;
	drawTile(xPos, yPos, TILE_WIDTH, TILE_HEIGHT, 0x0F, 0);
	
}
